/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.streets.eis.ext.rbac.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.streets.commons.util.GUID;
import org.streets.commons.util.StringUtils;
import org.streets.database.Limits;
import org.streets.database.PV;
import org.streets.database.SQLBatcher;
import org.streets.database.SQLConnection;
import org.streets.database.datadict.ColumnDefinition;
import org.streets.database.datadict.DataDict;
import org.streets.database.datadict.TableDefinition;
import org.streets.eis.entity.Role;
import org.streets.eis.entity.User;
import org.streets.eis.ext.rbac.entities.RolePermission;
import org.streets.eis.ext.rbac.services.RBACAuthorizer;
import org.streets.eis.types.PermitTarget;
import org.streets.eis.types.PermitType;
import org.streets.context.definition.ContextDefinition;
import org.streets.context.definition.FunctionDefinition;
import org.streets.context.definition.ModuleDefinition;
import org.streets.wicket.HiveWebApplication;

/**
 *
 * @author dzb
 */
public class RBACAuthorizerImpl implements RBACAuthorizer {

	private final Logger logger;
	private final SQLConnection connection;
	
	public RBACAuthorizerImpl(Logger logger, SQLConnection connection) {
		this.logger = logger;
		this.connection = connection;
		if (connection==null){
			this.logger.error("RBACAuthorizerImpl Exception : connection cannot be null!");
		}
	}

	/**
	 * expr支持写法：type:values
	 * type:类型支持三种：organ、func、field，其中func也可以使用module代替
	 * values:多个表达式用逗号分隔，多表达式为采用逻辑与验证权限(即全部都有才返回true)；field类型的values同一表的可以放在[]里,同时需要指定要验证的权限值
	 * 如：
	 * 	organ:xxx  
	 * 			如果user对id为xxx的机构有权限返回true
	 * organ:xxx,aaa 
	 * 			只有user对id为xxx和aaa的机构都有权限才返回true
	 * func:xxx 
	 * 			如果user对id为xxx的功能或模块有权限返回true
	 * func:xxx,aaa  
	 * 			只有user对id为xxx和aaa的功能或模块都有权限才返回true
	 * field:tableA.fieldA.R 
	 * 			如果user对表tableA的字段fieldA有只读或完全控制权限返回true
	 * field:tableA.fieldA.W 
	 * 			如果user对表tableA的字段fieldA有完全控制权限返回true
	 * field:tableA.fieldA.R,tableA.fieldB.W 
	 * 			只有user对表tableA的字段fieldA有只读或完全控制权限、同时对fieldB有完全控制权限才返回true
	 * 
	 * @param user
	 * @param expr
	 */
	public boolean authorized(User user, String expr) {
		
		if (user == null) return false;

		if (StringUtils.isEmpty(expr)) return true;

		String userId = user.getUid();
		
		if (expr.startsWith("organ:")){
			
			String[] organIds = expr.substring("organ:".length()).split(",");
			for (String organId : organIds){
				if (!hasOrganPermission(userId, organId.trim())) return false;
			}
			return true;
			
		} else if (expr.startsWith("func:")){
			
			String[] funcIds = expr.substring("func:".length()).split(",");
			for (String funcId : funcIds){
				if (!hasFunctionPermission(userId, funcId.trim())) return false;
			}
			return true;
			
		} else if (expr.startsWith("module:")){
			
			String[] funcIds = expr.substring("module:".length()).split(",");
			for (String funcId : funcIds){
				if (!hasFunctionPermission(userId, funcId.trim())) return false;
			}
			return true;
			
		} else if (expr.startsWith("field:")){
			
			String[] fieldIds = expr.substring("field:".length()).split(",");
			for (String fieldExpr : fieldIds){
				fieldExpr = fieldExpr.trim();
				String[] fields = fieldExpr.split("\\.");
				if (fields.length!=3) return false;
				PermitType permit = "R".equals(fields[2]) ? PermitType.READONLY : PermitType.NONE;				
				if (!hasFieldPermission(userId, fields[0].trim(),fields[1].trim(),permit)) return false;
			}
			return true;
		}
		
		return false;
	}
	
	public void generateRoleAllPermissions(Role role){
		generateRolePermissions(role, PermitTarget.ORGAN);
		generateRolePermissions(role, PermitTarget.FUNC);
		generateRolePermissions(role, PermitTarget.FIELD);
	}
	
	public void generateRolePermissions(Role role, PermitTarget target) {
		
		String tableName;		
		
		if (PermitTarget.ORGAN.equals(target)) {
			tableName = "EIS_ROLE_ORGAN_PERMS";
		} else if (PermitTarget.FUNC.equals(target)) {
			tableName = "EIS_ROLE_FUNC_PERMS";
		} else if (PermitTarget.FIELD.equals(target)) {
			tableName = "EIS_ROLE_FIELD_PERMS";
		} else {
			return;
		}
		
		String clearSql  = String.format("DELETE FROM %s WHERE ROLE_ID='%s';", tableName, role.getId());
		
		String permissionSql = "SELECT * FROM EIS_ROLE_PERMISSIONS WHERE ROLE_ID=:rid and PERM_TARGET=:pt"; 
		List<RolePermission> rolePermissions = connection.query(permissionSql,RolePermission.class, Limits.none(), PV.nv("rid", role.getId()), PV.nv("pt", target.getName()));
		
		SQLBatcher batcher = null;
		
		if (PermitTarget.ORGAN.equals(target)) {
			List<String> organIds = getRolePermissionOrganIds(rolePermissions);
			batcher = connection.prepare("INSERT INTO EIS_ROLE_ORGAN_PERMS(ID,ROLE_ID,ORGAN_ID) VALUES(?,?,?)");
			for (String organId : organIds){
				batcher.add(new Object[]{GUID.nextUUID(), role.getId(), organId});
			}
		} else if (PermitTarget.FUNC.equals(target)) {
			List<String> funcIds = getRolePermissionFuncIds(rolePermissions);
			batcher = connection.prepare("INSERT INTO EIS_ROLE_FUNC_PERMS(ID,ROLE_ID,FUNC_ID) VALUES(?,?,?)");
			for (String funcId : funcIds){
				batcher.add(new Object[]{GUID.nextUUID(), role.getId(), funcId});
			}
		} else if (PermitTarget.FIELD.equals(target)) {
			tableName = "EIS_ROLE_FIELD_PERMS";
			Map<String,String> fieldIds = getRolePermissionFieldIds(rolePermissions);
			batcher = connection.prepare("INSERT INTO EIS_ROLE_FIELD_PERMS(ID,ROLE_ID,ENTITY_NAME,FIELD_NAME,PERM_VALUE) VALUES(?,?,?,?,?)");
			for (String fieldId : fieldIds.keySet()){
				String[] field = fieldId.split("\\.");
				batcher.add(new Object[]{GUID.nextUUID(), role.getId(), field[0], field[1], fieldIds.get(fieldId)});
			}
		} 
		
		//execute
		connection.execute(clearSql);
		batcher.execute();
	}
	
	public boolean removeRolePermissions(Role... roles){
		String sql = "";
		for (Role role : roles) {
			sql += String.format(
				"DELETE FROM EIS_ROLE_FIELD_PERMS WHERE ROLE_ID='%s';"+
				"DELETE FROM EIS_ROLE_FUNC_PERMS WHERE ROLE_ID='%s';"+
				"DELETE FROM EIS_ROLE_ORGAN_PERMS WHERE ROLE_ID='%s';"+
				"DELETE FROM EIS_ROLE_RECORD_PERMS WHERE ROLE_ID='%s';"+
				"DELETE FROM EIS_ROLE_PERMISSIONS WHERE ROLE_ID='%s';",
				role.getId(), role.getId(), role.getId(), role.getId(), role.getId());
		}
		
		int[] result = connection.execute(sql);
		
		return result.length > 0;
	}
	
//	public CharSequence getRemoveRoleAllPermissionsSql(String roleId){
//		return String.format(  
//				"DELETE FROM EIS_ROLE_FIELD_PERMS WHERE ROLE_ID='%s';"+
//				"DELETE FROM EIS_ROLE_FUNC_PERMS WHERE ROLE_ID='%s';"+
//				"DELETE FROM EIS_ROLE_ORGAN_PERMS WHERE ROLE_ID='%s';"+
//				"DELETE FROM EIS_ROLE_RECORD_PERMS WHERE ROLE_ID='%s';"+
//				"DELETE FROM EIS_ROLE_PERMISSIONS WHERE ROLE_ID='%s';",
//				roleId,roleId,roleId,roleId,roleId);
//	}
	
	public boolean removeRolePermissions(Role role, PermitTarget target){
		
		String sql = String.format(
				"DELETE FROM EIS_ROLE_%s_PERMS WHERE ROLE_ID='%s';"+
				"DELETE FROM EIS_ROLE_PERMISSIONS WHERE ROLE_ID='%s' AND PERM_TARGET='%s';",
		
		target.getName(), role.getId(), role.getId(), target.getName());

		int[] result = connection.execute(sql);
		
		return result.length > 0;
	}
	
//	public CharSequence getRemoveRolePermissionsSql(String roleId,PermitTarget target){
//		return String.format(
//				"DELETE FROM EIS_ROLE_%s_PERMS WHERE ROLE_ID='%s';"+
//				"DELETE FROM EIS_ROLE_PERMISSIONS WHERE ROLE_ID='%s' AND PERM_TARGET='%s';",
//				target.getName(),roleId,roleId,target.getName());
//	}
	
	public boolean isRootUser(User user){
		
		return user!=null && ROOT_USER.equalsIgnoreCase(user.getUid());
	}
	
	public String generateUserOrganPermitSql(User user){

		if (ROOT_USER.equalsIgnoreCase(user.getUid())){
			return "SELECT ID FROM EIS_ORGAN";
		} else {
			return String.format("SELECT ORGAN_ID FROM EIS_ROLE_ORGAN_PERMS WHERE "+
					" ROLE_ID IN (SELECT ROLE_ID FROM EIS_POS_ROLE_MAP WHERE "+
					"		POS_ID IN (SELECT POS_ID FROM EIS_EMPLOYEE WHERE USER_ID='%s')"+
					" )" , user.getUid());
		}
	}
	
	/**
	 * 获得角色有权限的机构id
	 * @param conn
	 * @param rolePermissions
	 * @return
	 */
	private final List<String> getRolePermissionOrganIds(List<RolePermission> rolePermissions) {
		
		List<String> organIds = new ArrayList<String>();
		
		for (RolePermission rolePermission : rolePermissions){		
			if (rolePermission.incChild) {

				String organSql = "SELECT ID FROM EIS_ORGAN START WITH ID=? CONNECT BY PARENT_ID=PRIOR ID";				
				List<String> ids = connection.column(organSql, Limits.none(), PV.of(rolePermission.targetValue));

				for (String organId : ids) {
					if (!organIds.contains(organId)){
						organIds.add(organId);
					}
				}
			} else {
				if (!organIds.contains(rolePermission.targetValue)){
					organIds.add(rolePermission.targetValue);
				}
			}
		}
		return organIds;
	}
	
	/**
	 * for getRolePermissionFieldIds 增加一个字段权限（取权限值最小的）
	 * @param fieldIds
	 * @param entityName
	 * @param fieldName
	 * @param permValue
	 */
	private void addField(Map<String,String> fieldIds, String entityName, String fieldName, String permValue) {
		String key = entityName + "." + fieldName;
		if (fieldIds.containsKey(key)){
			String permit = fieldIds.get(key);
			if (Integer.valueOf(permit).intValue() > Integer.valueOf(permValue).intValue()) {
				fieldIds.put(key, permValue);
			}
		} else {
			fieldIds.put(key, permValue);
		}
	}
	
	/**
	 * for getRolePermissionFieldIds 增加一个表权限
	 * @param fieldIds
	 * @param def
	 * @param permValue
	 */
	private void addEntity(Map<String,String> fieldIds, TableDefinition def, String permValue) {
		for (ColumnDefinition column : def.getColumns()){
			addField(fieldIds,def.getName(),column.getName(),permValue);
		}
	}
	
	/**
	 * 获得角色有权限的字段id
	 * @param conn
	 * @param rolePermissions
	 * @return
	 */
	private final Map<String,String> getRolePermissionFieldIds(List<RolePermission> rolePermissions){
		Map<String,String> fieldIds = new HashMap<String,String>();
		DataDict dict = HiveWebApplication.get().getRegistry().service(DataDict.class);
		
		for (RolePermission rolePermission : rolePermissions){
			if (rolePermission.permValue.equals(String.valueOf(PermitType.NONE.value()))) continue;
			
			String value = rolePermission.targetValue;
			String entityName = "";
			String fieldName = "";
			if (value.startsWith("group:")){
				value = value.substring("group:".length());
				for(TableDefinition table : dict.getTableDefs()){
					addEntity(fieldIds,table,rolePermission.permValue);
				}
			} else {
				if (value.indexOf(".")>0){
					entityName = value.split("\\.")[0];
					fieldName = value.split("\\.")[1];
				} else {
					entityName = value;
					if (!rolePermission.incChild) continue;
				}
			}
			
			if (StringUtils.isEmpty(fieldName)){
				addEntity(fieldIds,dict.getTableDef(entityName), rolePermission.permValue);
			} else {
				addField(fieldIds,entityName,fieldName,rolePermission.permValue);
			}
		}
		return fieldIds;
	}
	
	/**
	 * for getRolePermissionFuncIds 生成全面模块功能的map,方便定位
	 * @param funcIds
	 * @param module
	 */	
	private final void generateModuleMap(Map<String, Object> modules, ModuleDefinition def) {
		modules.put(def.getId(), def);
		List<FunctionDefinition> funcs = def.getFunctions();
		for(FunctionDefinition func : funcs){
			modules.put(func.getId(), func);
		}
		for (ModuleDefinition sub : def.getSubModules()){
			generateModuleMap(modules,sub);
		}		
	}

	private final boolean addFunctionId(List<String> funcIds, String funcId) {
		if (!funcIds.contains(funcId)) {
			funcIds.add(funcId);
			return true;
		} 
		return false;
	}
	
	/**
	 * for getRolePermissionFuncIds 增加整个模块（自动添加其fullpath，子模块及功能项）
	 * @param funcIds
	 * @param module
	 */	
	private final void addModule(List<String> funcIds, ModuleDefinition module) {

		if (addFunctionId(funcIds,module.getId())) {
			ModuleDefinition def = module.getParent();
			while (def!=null && addFunctionId(funcIds, def.getId()) ){
				def = def.getParent();
			}
		}

		for(FunctionDefinition func : module.getFunctions()){
			addFunctionId(funcIds,func.getId());
		}
		
		for (ModuleDefinition sub : module.getSubModules()){
			addFunctionId(funcIds, sub.getId());
			addModule(funcIds, sub);
		}
	}

	/**
	 * for getRolePermissionFuncIds 增加功能项（自动添加其fullpath）
	 * @param funcIds
	 * @param func
	 */
	private final void addFunction(List<String> funcIds, FunctionDefinition func) {
		if (addFunctionId(funcIds,func.getId())) {
			ModuleDefinition module = func.getModule();
			if (addFunctionId(funcIds,module.getId())) {
				ModuleDefinition def = module.getParent();
				while (def!=null && addFunctionId(funcIds,def.getId()) ){
					def = def.getParent();
				}
			}
		}
	}

	/**
	 * 获得角色有权限的功能id(包括其路径上的模块)
	 * @param rolePermissions
	 * @return
	 */
	private final List<String> getRolePermissionFuncIds(List<RolePermission> rolePermissions) {
		List<String> funcIds = new ArrayList<String>();
		
		Map<String,Object> modules = new HashMap<String,Object>();
		ContextDefinition definition = HiveWebApplication.get().getRegistry().service(ContextDefinition.class);
		List<ModuleDefinition> mds = definition.getModules();
		for (ModuleDefinition md : mds) { 			
			if (md.getType() != 1) continue;
			generateModuleMap(modules,md);
		}
		
		for (RolePermission rolePermission : rolePermissions){
			Object obj = modules.get(rolePermission.targetValue);
			if (obj instanceof ModuleDefinition) {
				ModuleDefinition module = (ModuleDefinition) obj;
				if (rolePermission.incChild.booleanValue()){
					addModule(funcIds,module);
				} else {					
					if (addFunctionId(funcIds,module.getId())) {
						ModuleDefinition def = module.getParent();
						while (def!=null && addFunctionId(funcIds,def.getId()) ){
							def = def.getParent();
						}
					}
				}				
			} else {
				addFunction(funcIds,(FunctionDefinition)obj);	
			}
		}
		return funcIds;
	}
	
	/**
	 * 判断是否有指定机构权限
	 * @param userId
	 * @param funcId
	 * @return
	 */
	private boolean hasOrganPermission(String userId,String organId){
		if (ROOT_USER.equalsIgnoreCase(userId)){
			return true;
		}
		String sql = "SELECT ID FROM EIS_ROLE_ORGAN_PERMS WHERE ORGAN_ID=? "+
		" 	AND ROLE_ID IN (SELECT ROLE_ID FROM EIS_POS_ROLE_MAP WHERE "+
		"		POS_ID IN (SELECT POS_ID FROM EIS_EMPLOYEE WHERE USER_ID=?)"+
		" )";
		return connection.count(sql, PV.of(organId, userId)) > 0;
	}
	
	/**
	 * 判断是否有指定功能或模块权限
	 * @param userId
	 * @param funcId
	 * @return
	 */
	private boolean hasFunctionPermission(String userId, String funcId){
		
		if (ROOT_USER.equalsIgnoreCase(userId)){
			return true;
		}
		
		String sql = "SELECT ID FROM EIS_ROLE_FUNC_PERMS WHERE FUNC_ID=? "+
		" 	AND ROLE_ID IN (SELECT ROLE_ID FROM EIS_POS_ROLE_MAP WHERE "+
		"		POS_ID IN (SELECT POS_ID FROM EIS_EMPLOYEE WHERE USER_ID=?)"+
		" )";

		return connection.count(sql, PV.of(funcId, userId)) > 0;
	}
	
	/**
	 * 判断是否有指定字段权限（权限值大于等于传入参数）
	 * @param userId
	 * @param entityName
	 * @param fieldName
	 * @param permit
	 * @return
	 */
	private boolean hasFieldPermission(String userId, String entityName, String fieldName, PermitType permit) {
		
		if (ROOT_USER.equalsIgnoreCase(userId)){
			return true;
		}
		String sql = "SELECT PERM_VALUE FROM EIS_ROLE_FIELD_PERMS WHERE ENTITY_NAME=? AND FIELD_NAME=? "+
		" 	AND ROLE_ID IN (SELECT ROLE_ID FROM EIS_POS_ROLE_MAP WHERE "+
		"		POS_ID IN (SELECT POS_ID FROM EIS_EMPLOYEE WHERE USER_ID=?)"+
		" )" ;
		
		String value = connection.column(sql, PV.of(entityName, fieldName, userId));
		
		return StringUtils.isEmpty(value) || (Integer.valueOf(value).intValue()>= permit.value() );
	}	
}
